
	robotEnabled = false; /* ignore */
	showDebug = false; /* Enable debug log div -- NOTE: ALSO RUINS RESIZE ON CHROME */

	var interval = false; /* interval for ugly AutoUpdateRobotPatch */
	var lastrobotupdate = 1; /* for ugly AutoUpdateRobotPatch */
	
	var ROBOTID = 'todoit-reminder@appspot.com'; /* robot's participant id */


// TEMPFIX: THE ONE IN tasks_gui.js CAUSES WRONG TASK COMPLETED, WASTED WAY TOO MUCH TIME ON THIS.
/* NOTE: CHROME'S ARRAY.SORT() IS AN UNSTABLE SORT (FOR UP TO <CONST> ITEMS?!?!), FIREFOX INSERTS IN END ON EQUAL COMPARE, CHROME DOES NOT ALWAYS !!!!!!!!!
         THIS CAUSES SORTING BY VISIBLE_TASKS TO LOSE SYNC WITH THE DISPLAYED DIVS (ROWS) !!!!!!!
         FIX: RE-READ DISPLAYED DIVS TO GET VISIBLE_TASKS ORDER AND WORK ACCORDING TO IT, OR, IN ORDER TO ACHIEVE VENDOR INDEPENDENT SORTING -- WRITE OUR OWN SORT() (WHICH WILL BE SLOWER, BECAUSE THEY CAN USE NATIVE CODE).
*/
taskComparer = function(t1, t2) {
        
        var dueDateDelta;
        
        if(t1.dueDate && t2.dueDate) // != null
                dueDateDelta = t2.dueDate.getTime() - t1.dueDate.getTime();
        //else if (t1.dueDate) dueDateDelta = -1;
        //else if (t2.dueDate) dueDateDelta =  1;
        else 
                dueDateDelta = 0;
        if (dueDateDelta == 0) dueDateDelta = t1.id < t2.id ? -1 : 1;
                
        switch (sortBy) {
                case "name": 
									var nameDelta = t1.title.toLowerCase().localeCompare(t2.title.toLowerCase()); // return (t2.title < t1.title) - (t1.title < t2.title); // string compare
									return (nameDelta != 0) ? nameDelta : dueDateDelta;
                case "priority": 
                	var prioDelta = t1.priority - t2.priority;
                	return (prioDelta != 0) ? prioDelta : dueDateDelta; // second sort: dueDate
                case "duedate": return dueDateDelta;
                case "duedate-r": return (-dueDateDelta);
                case "progress": 
                	var d = t2.progress - t1.progress;
                	return (d != 0) ? d : dueDateDelta;
        }
};


var dbgmsg = false;
var firstrender = true;

function logm(obj, mmsg /* force minimsg */) {
	if (window.console && console.log && !mmsg) console.log(obj);
	else if (dbgmsg) {
		var d = new Date();
		dbgmsg.createDismissibleMessage(d.getTime() + ": " + obj.toString());
	}
}

function log(o,m) {logm(o,1);}

function setVal(key, val) {
	var delta = {};
	delta[key] = val;
	wave.getState().submitDelta(delta);
}

function getVal(key) {
	return wave.getState().get(key);
}

function onStateChanged(state) {
	render(state);
}


function render(state) {
	log('render() called.');

	var changed_tasks      = []; // all tasks with version changed
	var changed_tasks_vers = [];
	
	var added_tasks     = [];
	var updated_tasks   = [];
	var completed_tasks = [];
	var deleted_tasks   = [];

	logm('render: get all tasks:');
	
	// get all tasks
	var allKeys = state.getKeys();		
	for (var i = 0; i < allKeys.length; i++) {
		var key = allKeys[i];
		if (key.indexOf('vtask') == 0) { // get task version key
			var curr_tver = state.get(key);
			var prev_tver = all_tasks_vers[key];
			if (prev_tver != curr_tver) { // this task changed, get the new ver

				var uid = key.substr(1);
				var newtask_ser = state.get(uid,'');
				var newtask = null;
				if (newtask_ser != '') {
					var newtask_deser = JSON.parse(newtask_ser);
					newtask = reviveTask(newtask_deser);
				}
				
				if (!prev_tver) {	// task added
					all_tasks_vers[key] = curr_tver;
					if (newtask && !newtask.isDeleted()) {
						added_tasks.push(newtask);
						all_tasks.push(newtask);
						logm(newtask);
					}
				}
				else { // task changed otherwise
					changed_tasks_vers.push(key);
					changed_tasks.push(newtask);
				}
			}
		}
	}
	
	logm('render: check changes:');
	logm(changed_tasks);
	
	// check changes
	for (var i = 0; i < all_tasks.length; i++) {
		for (var j = 0; j < changed_tasks.length; j++) {
			if (changed_tasks_vers[j] == 'v'+all_tasks[i].id) {
			
				var curr_tver = state.get(changed_tasks_vers[j]);
				var prev_tver = all_tasks_vers[changed_tasks_vers[j]];
				if (parseInt(curr_tver) > parseInt(prev_tver)) {
				
					all_tasks_vers[changed_tasks_vers[j]] = curr_tver;
					var newtask = changed_tasks[j];
					var oldtask = all_tasks[i];
					//logm(newtask);
					
					if (newtask == null || newtask.isDeleted()) { // deleted task
						deleted_tasks.push(newtask ? newtask : oldtask);
						all_tasks.splice(i, 1);
						--i;
					}
					else { // what changed?
					
						// newly completed?
						var newcomplete = newtask.isCompleted() && !oldtask.isCompleted();
						if (newcomplete)
							completed_tasks.push(newtask);
						
						// other updates? // TODO: if (newcomplete) also add to updated?
						var cn = compareTasks(newtask, oldtask);
						if ((cn > 4 && newcomplete) || (cn > 0 && !newcomplete)) {
							updated_tasks.push(newtask); // otherwise updated
						}
						all_tasks[i] = newtask;
					}
					changed_tasks.splice(j, 1);
					changed_tasks_vers.splice(j, 1);
					break;
				}
			}
		}
	}

	// get task discussion waves
	var allKeys = state.getKeys();
	for (var i = 0; i < allKeys.length; i++) {
		var key = allKeys[i];
		if (key.indexOf('wtask') == 0) { // get task discussion wave
			var curr_twave = state.get(key);
			all_tasks_waves[key] = curr_twave;
		}
	}


	logm('render: calling UI...');

	
	
	if (firstrender) {
		gui_reload_list(); // uses all_tasks[]
		firstrender = false;
//		if (all_tasks.length == 0) todoit_welcome();
	} else {
		if (added_tasks.length > 0 || updated_tasks.length > 0 ||
		    completed_tasks.length > 0 || deleted_tasks.length > 0)
			gui_update_callback(added_tasks, updated_tasks, completed_tasks, deleted_tasks);
	}

	log('render() done.')
}

function todoit_welcome() { // welcome on first use/reset
	var newt = new task();
	newt.title = "Welcome to ToDoIt! Start by adding tasks above.";
	newt.tags = ['Welcome'];
	newt.dueDate = new Date();
	newt.lastModifier = 'ToDoIt';
	newt.link = 'http://code.google.com/p/todoit-wave/';
	//gui_update_callback([newt],[],[],[]); // just show to user // TEMPFIX: ALSO CAUSES COMPLETE WRONG TASK BUG
	if (wave.getHost().getId() == wave.getViewer().getId()) addTask(newt);
}

function isInsideWave(){
	
	return (typeof wave != "undefined") && wave.isInWaveContainer();
	
}


/* ugly patch to trigger state updates at set intervals for robot to see us */
function AutoUpdateRobotPatch() {
	if (!interval) {
		interval = setInterval(function() {
			var newrobotupdate = getVal('_lastrobotupdate');
			newrobotupdate = newrobotupdate ? parseInt(newrobotupdate) : 1;
			if (newrobotupdate <= lastrobotupdate) {
				setVal('_lastrobotupdate', newrobotupdate + 1);
			}
			lastrobotupdate = newrobotupdate + 1;
		}, 60000);
	}
}


function init() {
	
	init_todoit_gui();
	
	if (isInsideWave()) {
		wave.setStateCallback(onStateChanged);
		//wave.setModeCallback(onModeChanged);
		//wave.setParticipantCallback(onParticipantChanged);
	}

	if (showDebug) {
		$(document).append('<div id="debugDiv"></div>');
		dbgmsg = new gadgets.MiniMessage(__MODULE_ID__, $('#debugDiv')[0]);
		dbgmsg.createDismissibleMessage('Debug log::');
	}

	initCli();
	initDetailsView();
	
	//AutoUpdateRobotPatch();
	
	logm("Todoit-wave gadget - init done @ " + new Date());
}



if(typeof gadgets != "undefined") // is inside gadget container?
	gadgets.util.registerOnLoadHandler(init);
else
	$(document).ready(init); // for local debug

